#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
天翼云盘文件操作模块
基于tem目录下的file_operations.py重构
"""

import os
import hashlib
import json
import time
import requests
from typing import Dict, List, Optional, Any, Union, BinaryIO
from urllib.parse import urlparse, parse_qs
from .utils import FormatUtils, TimeUtils


class FileOperations:
    """天翼云盘文件操作类"""
    
    def __init__(self, client):
        """初始化文件操作
        
        Args:
            client: 天翼云盘客户端实例
        """
        self.client = client
        self.logger = client.logger
        self.session = client.session
    
    def get_files(self, folder_id: str = None, page_num: int = 1, page_size: int = 1000) -> Optional[Dict[str, Any]]:
        """获取文件列表
        
        Args:
            folder_id: 文件夹ID，None表示根目录
            page_num: 页码，从1开始
            page_size: 每页大小
        
        Returns:
            Dict[str, Any]: 文件列表数据
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        if folder_id is None:
            folder_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/listFiles.action"
        params = {
            'folderId': folder_id,
            'pageNum': page_num,
            'pageSize': page_size,
            'mediaType': 0,
            'iconOption': 5,
            'orderBy': 'lastOpTime',
            'descending': 'true'
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return {
                        'files': result.get('fileListAO', {}).get('fileList', []),
                        'folders': result.get('fileListAO', {}).get('folderList', []),
                        'total': result.get('fileListAO', {}).get('count', 0),
                        'page_num': page_num,
                        'page_size': page_size
                    }
                else:
                    self.logger.error(f"获取文件列表失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取文件列表请求失败: {e}")
        
        return None
    
    def get_file_info(self, file_id: str) -> Optional[Dict[str, Any]]:
        """获取文件详细信息
        
        Args:
            file_id: 文件ID
        
        Returns:
            Dict[str, Any]: 文件信息
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        url = "https://api.cloud.189.cn/getFileInfo.action"
        params = {
            'fileId': file_id
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return result.get('fileInfo', {})
                else:
                    self.logger.error(f"获取文件信息失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取文件信息请求失败: {e}")
        
        return None
    
    def get_download_url(self, file_id: str) -> Optional[str]:
        """获取文件下载链接
        
        Args:
            file_id: 文件ID
        
        Returns:
            str: 下载链接
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        url = "https://api.cloud.189.cn/getFileDownloadUrl.action"
        params = {
            'fileId': file_id,
            'dt': 1,
            'flag': 1
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return result.get('fileDownloadUrl')
                else:
                    self.logger.error(f"获取下载链接失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取下载链接请求失败: {e}")
        
        return None
    
    def download_file(self, file_id: str, save_path: str = None, filename: str = None) -> Union[bool, bytes, None]:
        """下载文件
        
        Args:
            file_id: 文件ID
            save_path: 保存路径（文件路径或目录路径），None表示返回文件内容
            filename: 文件名（当save_path为目录时使用）
        
        Returns:
            Union[bool, bytes, None]: 保存到文件返回bool，返回内容返回bytes，失败返回None
        """
        download_url = self.get_download_url(file_id)
        if not download_url:
            return None if save_path is None else False
        
        try:
            response = self.session.get(download_url, stream=True)
            if response.status_code == 200:
                # 如果save_path为None，返回文件内容
                if save_path is None:
                    content = b''
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            content += chunk
                    self.logger.info(f"文件内容下载成功，大小: {len(content)} 字节")
                    return content
                
                # 否则保存到文件
                # 确定保存路径
                if os.path.isdir(save_path):
                    if not filename:
                        # 获取文件信息以确定文件名
                        file_info = self.get_file_info(file_id)
                        if not file_info:
                            self.logger.error("无法获取文件信息")
                            return False
                        filename = file_info.get('fileName', f'file_{file_id}')
                    save_path = os.path.join(save_path, filename)
                
                os.makedirs(os.path.dirname(save_path), exist_ok=True)
                with open(save_path, 'wb') as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                self.logger.info(f"文件下载成功: {save_path}")
                return True
            else:
                self.logger.error(f"下载失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"下载文件失败: {e}")
        
        return None if save_path is None else False
    
    def make_dir(self, parent_id: str, folder_name: str) -> Optional[str]:
        """创建文件夹
        
        Args:
            parent_id: 父文件夹ID
            folder_name: 文件夹名称
        
        Returns:
            str: 创建成功返回文件夹ID，失败返回None
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        if parent_id is None:
            parent_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/createFolder.action"
        params = {
            'parentFolderId': parent_id,
            'folderName': folder_name
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "POST")
        
        try:
            response = self.session.post(url, data=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    folder_id = result.get('id')
                    self.logger.info(f"文件夹创建成功: {folder_name} (ID: {folder_id})")
                    return folder_id
                else:
                    self.logger.error(f"创建文件夹失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"创建文件夹请求失败: {e}")
        
        return None
    
    def delete_file(self, file_id: str, file_name: str = "", is_folder: bool = False) -> bool:
        """删除文件或文件夹
        
        Args:
            file_id: 文件ID
            file_name: 文件名（可选）
            is_folder: 是否为文件夹
        
        Returns:
            bool: 删除是否成功
        """
        if not self.client.check_login_status():
            if not self.client.login():
                self.logger.error("删除失败：用户未登录")
                return False
        
        self.logger.info(f"开始删除{'文件夹' if is_folder else '文件'}: {file_name} (ID: {file_id})")
        
        url = "https://api.cloud.189.cn/batch/createBatchTask.action"
        
        # 构建删除任务参数，参考Go源码的BatchTaskInfo结构
        task_info = {
            'fileId': file_id,
            'fileName': file_name,
            'isFolder': 1 if is_folder else 0
        }
        
        params = {
            'type': 'DELETE',
            'taskInfos': json.dumps([task_info])
        }
        
        # 添加必要的客户端参数
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "POST")
        
        try:
            # 创建删除任务
            self.logger.debug(f"发送删除请求: URL={url}, params={params}")
            response = self.session.post(url, data=params, headers=headers)
            
            if response.status_code != 200:
                self.logger.error(f"创建删除任务失败，状态码: {response.status_code}")
                self.logger.error(f"响应内容: {response.text}")
                return False
            
            result = response.json()
            self.logger.debug(f"删除任务创建响应: {result}")
            
            if result.get('res_code') != 0 and result.get('resCode') != 0:
                error_msg = result.get('res_message', result.get('resMessage', '未知错误'))
                self.logger.error(f"创建删除任务失败: {error_msg}")
                return False
            
            task_id = result.get('taskId')
            if not task_id:
                self.logger.error("获取任务ID失败")
                return False
            
            self.logger.info(f"删除任务已创建，任务ID: {task_id}")
            
            # 等待删除任务完成
            success = self._wait_batch_task("DELETE", task_id)
            
            if success:
                self.logger.info(f"删除{'文件夹' if is_folder else '文件'}成功: {file_name}")
                # 删除成功后，清除回收站
                self.logger.info("开始清除回收站...")
                clear_params = {
                    'type': 'CLEAR_RECYCLE',
                    'taskInfos': json.dumps([task_info])
                }
                clear_params.update(self.client.client_suffix())
                
                clear_response = self.session.post(url, data=clear_params, headers=headers)
                if clear_response.status_code == 200:
                    clear_result = clear_response.json()
                    if clear_result.get('res_code') == 0 or clear_result.get('resCode') == 0:
                        clear_task_id = clear_result.get('taskId')
                        if clear_task_id:
                            clear_success = self._wait_batch_task("CLEAR_RECYCLE", clear_task_id)
                            if clear_success:
                                self.logger.info("回收站清除成功")
                            else:
                                self.logger.warning("回收站清除失败，但文件已删除")
                        else:
                            self.logger.warning("获取清除回收站任务ID失败")
                    else:
                        self.logger.warning(f"创建清除回收站任务失败: {clear_result.get('res_message', clear_result.get('resMessage'))}")
                else:
                    self.logger.warning(f"清除回收站请求失败，状态码: {clear_response.status_code}")
            else:
                self.logger.error(f"删除{'文件夹' if is_folder else '文件'}失败: {file_name}")
            
            return success
            
        except Exception as e:
            self.logger.error(f"删除文件异常: {e}")
            return False
    
    def _wait_batch_task(self, task_type: str, task_id: str, check_interval: float = 0.5, max_wait_time: int = 30) -> bool:
        """等待批处理任务完成
        
        Args:
            task_type: 任务类型
            task_id: 任务ID
            check_interval: 检查间隔（秒）
            max_wait_time: 最大等待时间（秒）
        
        Returns:
            bool: 任务是否成功完成
        """
        url = "https://api.cloud.189.cn/batch/checkBatchTask.action"
        
        start_time = time.time()
        while time.time() - start_time < max_wait_time:
            params = {
                'type': task_type,
                'taskId': task_id
            }
            params.update(self.client.client_suffix())
            
            headers = self.client.signature_header(url, "POST")
            
            try:
                response = self.session.post(url, data=params, headers=headers)
                if response.status_code == 200:
                    result = response.json()
                    self.logger.debug(f"任务状态检查响应: {result}")
                    if result.get('res_code') == 0 or result.get('resCode') == 0:
                        task_status = result.get('taskStatus')
                        self.logger.debug(f"任务状态: {task_status}")
                        if task_status == 4:  # 任务完成
                            self.logger.info(f"{task_type}任务完成")
                            return True
                        elif task_status == 2:  # 存在冲突
                            error_msg = result.get('res_message', result.get('resMessage', '存在冲突'))
                            self.logger.error(f"{task_type}任务冲突: {error_msg}")
                            return False
                        elif task_status == 3:  # 任务执行中
                            self.logger.debug(f"{task_type}任务执行中，继续等待...")
                            # 继续等待，不返回
                        elif task_status == 1:  # 任务初始化
                            self.logger.debug(f"{task_type}任务初始化中，继续等待...")
                            # 继续等待，不返回
                        else:
                            # 未知状态，记录详细信息
                            error_msg = result.get('res_message', result.get('resMessage', '未知错误'))
                            task_info = result.get('taskInfo', {})
                            failed_count = result.get('failedCount', 0)
                            success_count = result.get('successedCount', 0)
                            skip_count = result.get('skipCount', 0)
                            
                            self.logger.error(f"{task_type}任务未知状态 {task_status}: {error_msg}")
                            self.logger.error(f"任务统计 - 成功: {success_count}, 失败: {failed_count}, 跳过: {skip_count}")
                            
                            if task_info:
                                self.logger.error(f"任务详情: {task_info}")
                            
                            return False
                    else:
                        error_msg = result.get('res_message', result.get('resMessage', '未知错误'))
                        self.logger.error(f"检查任务状态失败: {error_msg}")
                        return False
                else:
                    self.logger.error(f"检查任务状态请求失败，状态码: {response.status_code}")
                    return False
            except Exception as e:
                self.logger.error(f"检查任务状态异常: {e}")
                return False
            
            time.sleep(check_interval)
        
        self.logger.error(f"{task_type}任务超时")
        return False
    
    def calculate_file_md5(self, file_data: Union[str, bytes, BinaryIO]) -> str:
        """计算文件MD5值
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
        
        Returns:
            str: MD5值（小写）
        """
        hash_md5 = hashlib.md5()
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
        elif isinstance(file_data, bytes):
            # 字节数据
            hash_md5.update(file_data)
        elif hasattr(file_data, 'read'):
            # 文件对象
            current_pos = file_data.tell() if hasattr(file_data, 'tell') else 0
            if hasattr(file_data, 'seek'):
                file_data.seek(0)
            
            for chunk in iter(lambda: file_data.read(4096), b""):
                hash_md5.update(chunk)
            
            # 恢复原始位置
            if hasattr(file_data, 'seek'):
                file_data.seek(current_pos)
        else:
            raise ValueError("不支持的文件数据类型")
        
        return hash_md5.hexdigest().lower()
    
    def calculate_slice_size(self, file_size: int) -> int:
        """计算分片大小
        
        Args:
            file_size: 文件大小（字节）
        
        Returns:
            int: 分片大小（字节）
        """
        if file_size <= 1024 * 1024 * 10:  # 10MB以下
            return 1024 * 1024  # 1MB
        elif file_size <= 1024 * 1024 * 100:  # 100MB以下
            return 1024 * 1024 * 5  # 5MB
        else:
            return 1024 * 1024 * 10  # 10MB
    
    def calculate_slice_md5(self, file_data: Union[str, bytes, BinaryIO], slice_size: int) -> List[str]:
        """计算文件分片MD5值
        
        Args:
            file_data: 文件数据
            slice_size: 分片大小
        
        Returns:
            List[str]: 分片MD5值列表
        """
        slice_md5_list = []
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, "rb") as f:
                while True:
                    chunk = f.read(slice_size)
                    if not chunk:
                        break
                    slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
        elif isinstance(file_data, bytes):
            # 字节数据
            for i in range(0, len(file_data), slice_size):
                chunk = file_data[i:i + slice_size]
                slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
        elif hasattr(file_data, 'read'):
            # 文件对象
            current_pos = file_data.tell() if hasattr(file_data, 'tell') else 0
            if hasattr(file_data, 'seek'):
                file_data.seek(0)
            
            while True:
                chunk = file_data.read(slice_size)
                if not chunk:
                    break
                slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
            
            # 恢复原始位置
            if hasattr(file_data, 'seek'):
                file_data.seek(current_pos)
        else:
            raise ValueError("不支持的文件数据类型")
        
        return slice_md5_list